﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Threading;

namespace oViT.FileWatcher
{
    public class FileProcessor
    {
        #region Variables
        private string processpath = string.Empty;
        private string mak = string.Empty;
        private string outputdirectory = string.Empty;
        private string fullsourcepath = string.Empty;
        private string sourcefilename = string.Empty;
        private string finalextension = string.Empty;
        private bool remove = false;
        private string output = string.Empty;

        private const int RETRY_MAX_TIME = 1800000; // 30 hour
        private const int RETRY_DELAY = 2000; // 10 seconds
        #endregion

        #region Properties
        /// <summary>
        /// application path to the file processor, tivodecode.exe
        /// </summary>
        public String ProcessPath 
        {
            get { return processpath; }
            set { processpath = value; }
        }

        /// <summary>
        /// unique TiVo identifier
        /// </summary>
        public String Mak
        {
            get { return mak; }
            set { mak = value; }
        }

        /// <summary>
        /// full path to directory of output files, .mpeg
        /// </summary>
        public String OutputDirectory
        {
            get { return outputdirectory; }
            set { outputdirectory = value; }
        }

        /// <summary>
        /// full path to source directory containing .tivo files
        /// </summary>
        public String FullSourcePath
        {
            get { return fullsourcepath; }
            set { fullsourcepath = value; }
        }

        /// <summary>
        /// file name of source file, [something].tivo
        /// </summary>
        public String SourceFileName
        {
            get { return sourcefilename; }
            set { sourcefilename = value; }
        }

        /// <summary>
        /// final file extension, right now, only .mpeg
        /// </summary>
        public String FinalExtension
        {
            get { return finalextension; }
            set { finalextension = value; }
        }

        /// <summary>
        /// remove the source file after processing
        /// </summary>
        public Boolean Remove
        {
            get { return remove;}
            set { remove = value;}
        }

        /// <summary>
        /// the full path to the file just output from processing
        /// </summary>
        public String Output
        {
            get { return output; }
            set { output = value; }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// default constructor
        /// </summary>
        /// <param name="processpath">application path to the file processor</param>
        /// <param name="mak">MAK id</param>
        /// <param name="outputdirectory">full path to directory of output files</param>
        /// <param name="fullsourcepath">full path to source directory</param>
        /// <param name="sourcefilename">file name of source file</param>
        /// <param name="finalextension">final file extension</param>
        /// <param name="remove">remove the source file after processing</param>
        public FileProcessor(string processpath, string mak, string outputdirectory,
            String fullsourcepath, String sourcefilename, String finalextension, bool remove) 
        {
            ProcessPath = processpath;
            Mak = mak;
            OutputDirectory = outputdirectory;
            FullSourcePath = fullsourcepath;
            SourceFileName = sourcefilename;
            FinalExtension = finalextension;
            Remove = remove;
        }
        #endregion

        #region Methods
        /*public FileProcessor BeginProcessing()
        {
            FileInfo fileprocessorFI = new FileInfo(this.ProcessPath);
            FileInfo destFI = new FileInfo(this.OutputDirectory + "\\" + FormatFileName(this.SourceFileName, this.FinalExtension));
            FileInfo sourceFI = new FileInfo(this.FullSourcePath);

            if (!destFI.Exists)
            {
                ProcessFile(fileprocessorFI, destFI, sourceFI, this.FinalExtension, this.Mak);
                Output = destFI.FullName;
            }
            else
            {
                Output = String.Format("Output file '{0}' already exists.\n\rDone.", destFI.Name);
            }
            return this;
        }*/

        /// <summary>
        /// converts the source files with the given application processor and outputs it to the given directory
        /// </summary>
        /// <returns>an instance of this object</returns>
        public FileProcessor BeginProcessing()
        {
            FileInfo fileprocessorFI = new FileInfo(this.ProcessPath);

            if (String.IsNullOrEmpty(mak))
            {
                throw new ArgumentException("MAK number is missing!");
            }

            // make sure the file processor app exists
            if (fileprocessorFI.Exists)
            {
                FileInfo destFI = new FileInfo(this.OutputDirectory + "\\" + FormatFileName(this.SourceFileName, this.FinalExtension));

                // check that the to-be-created file doesn't already exist
                if (!destFI.Exists)
                {
                    FileInfo sourceFI = new FileInfo(this.FullSourcePath);

                    DateTime receivedAt = DateTime.Now;
                    bool timedOut = false;
                    bool processed = false;

                    // check to see if we can read this file, if not, the .tivo file is probably being copied to this 
                    // directory and not finished yet
                    while (!(timedOut || processed))
                    {
                        if (CanReadFile(sourceFI.FullName))
                        {
                            // process the file
                            ProcessFile(fileprocessorFI, destFI, sourceFI, this.FinalExtension, this.Mak);

                            if (remove)
                            {
                                Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(sourceFI.FullName, Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs, Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
                            }

                            processed = true;
                            Output = destFI.FullName;
                        }
                        else
                        {
                            TimeSpan timeElapsed = DateTime.Now - receivedAt;

                            if (timeElapsed.TotalMilliseconds > RETRY_MAX_TIME)
                            {
                                timedOut = true;
                            }
                            else
                            {
                                Thread.Sleep(RETRY_DELAY);
                            }
                        }
                    }
                }
                else
                {
                    //throw new ArgumentException(String.Format("Output file '{0}' alread exists!", destFI.Name));
                    Output = String.Format("Output file '{0}' alread exists!", destFI.Name);
                }
            }
            else
            {
                //throw new ArgumentException("Process does not exist!");
                Output = "File Processor does not exist!";
            }
            return this;
        }

        /// <summary>
        /// replaces all occurances of the space character with an underscore
        /// </summary>
        /// <param name="name">file name to format</param>
        /// <param name="extension">file extension</param>
        /// <returns>formatted file name</returns>
        private String FormatFileName(String name, String extension)
        {
            FileInfo fi = new FileInfo(name);

            string ext = fi.Extension;

            return fi.Name.Replace(ext, extension).Replace(" ", "_");
        }

        /// <summary>
        /// Returns true if the specified file has completed uploading and is ready for 
        /// processing.
        /// </summary>
        /// <param name="filename">file name to check if it's readable</param>
        /// <returns>true=can read the file;false=file is not readable</returns>
        private bool CanReadFile(string filename)
        {
            //
            // If the file can be opened for exclusive access it means that the file
            // is no longer locked by the OS and has completed uploading.
            //
            try
            {
                using (FileStream inputStream = File.Open(filename, FileMode.Open,
                FileAccess.Read,
                FileShare.None))
                {
                    return true;
                }
            }
            catch (IOException)
            {
                return false;
            }
        }

        /// <summary>
        /// starts the conversion process; launches the tivodecode.exe from the command line and waits for it to complete
        /// </summary>
        /// <param name="fileprocessorFI">full path to the process path (tivodecode.exe)</param>
        /// <param name="outputFI">full path to directory where output file (.mpeg) is sent</param>
        /// <param name="sourcefileFI">full path to .tivo file</param>
        /// <param name="finalextension">file extension for final output (.mpeg)</param>
        /// <param name="mak">unique TiVo id</param>
        private void ProcessFile(FileInfo fileprocessorFI, FileInfo outputFI, FileInfo sourcefileFI, string finalextension, string mak)
        {
            Process p = new Process();
            p.StartInfo.WorkingDirectory = fileprocessorFI.DirectoryName;
            p.StartInfo.FileName = fileprocessorFI.Name;
            p.StartInfo.Arguments = String.Format("-m {0} -o \"{1}\" \"{2}\"", mak, outputFI.FullName, sourcefileFI.FullName);
            //p.StartInfo.CreateNoWindow = true;

            // need to set this to false to redirect output
            //p.StartInfo.UseShellExecute = false;
            //p.StartInfo.RedirectStandardOutput = true;

            p.Start();

            // read all the output
            // here we could just read line by line and display it
            // in an output window
            //String output = p.StandardOutput.ReadToEnd();

            // send the output to the caller
            //if (FileProcessed != null)
            //    FileProcessed(sender, new WatcherEventArgs(e, output));

            p.WaitForExit();
        }
        #endregion
    }
}
